package com.yanqu.road.server.manager.curio;

import com.yanqu.road.dao.impl.curio.CurioLotteryNoticeDataDaoImpl;
import com.yanqu.road.entity.curio.CurioLotteryNoticeData;
import com.yanqu.road.entity.enums.eServerState;
import com.yanqu.road.entity.player.UserBaseInfo;
import com.yanqu.road.entity.servercenter.ServerGroup;
import com.yanqu.road.entity.servercenter.ServerInfo;
import com.yanqu.road.logic.pb.PlayerBasePb;
import com.yanqu.road.message.YanQuMessage;
import com.yanqu.road.pb.cangbao.CurioProto;
import com.yanqu.road.server.TempMgr;
import com.yanqu.road.server.logic.MessageHelper;
import com.yanqu.road.server.manager.user.CrossUserMgr;
import com.yanqu.road.server.manger.ServerGroupMgr;
import com.yanqu.road.server.manger.ServerListMgr;
import com.yanqu.road.server.protocol.Protocol;
import com.yanqu.road.utils.ConfigHelper;
import com.yanqu.road.utils.YanQuMessageUtils;
import org.apache.mina.util.ConcurrentHashSet;

import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class CrossCurioMgr extends TempMgr {

    private static Map<Integer, List<CurioLotteryNoticeData>> noticeMap;

    //groupId itemId maxScore
    private static Map<Integer, Map<Integer, BigInteger>> groupMaxCurioScoreMap = new ConcurrentHashMap<>();

    private static Set<Integer> needSyncGroup = new ConcurrentHashSet<>();

    //分组内历史达成数据
    //groupId taskId
    private static Map<Integer, Map<Integer, CurioProto.CurioItemMaxScoreUserTemp>> groupHistoryConditionMap = new ConcurrentHashMap<>();

    //需要上传（档位数据的区服 + 藏品最高分），分组变更，重新拉上来
    private static Set<Long> uploadServerSet = new ConcurrentHashSet<>();

    private static int maxCount = 200;

    @Override
    public boolean init() throws Exception {
        boolean reload = reload();
        if(reload){
            ServerGroupMgr.addReloadListener(() -> reloadNotice());
        }
        return reload;
    }

    private void reloadNotice() {
        synchronized (noticeMap){
            Map<Integer, List<CurioLotteryNoticeData>> tempMap = new HashMap<>(noticeMap);
            noticeMap.clear();
            for (Map.Entry<Integer, List<CurioLotteryNoticeData>> entry : tempMap.entrySet()) {
                for (CurioLotteryNoticeData data : entry.getValue()) {
                    int groupId = (int)data.getServerId();
                    ServerGroup serverGroup = ServerGroupMgr.getServerGroupByServerId(entry.getKey());
                    if(serverGroup != null && serverGroup.isCrossRank()) {
                        groupId = serverGroup.getGroupId();
                    }
                    if(!noticeMap.containsKey(groupId)){
                        noticeMap.put(groupId, new ArrayList<>());
                    }
                    synchronized (noticeMap.get(groupId)) {
                        noticeMap.get(groupId).add(data);
                    }
                }
            }
            for (Map.Entry<Integer, List<CurioLotteryNoticeData>> entry : noticeMap.entrySet()) {
                entry.getValue().sort(Comparator.comparingLong(CurioLotteryNoticeData::getTime));
            }
        }
        synchronized (groupMaxCurioScoreMap){
            groupMaxCurioScoreMap.clear();
        }

        synchronized (groupHistoryConditionMap){
            groupHistoryConditionMap.clear();
        }

        synchronized (uploadServerSet) {
            uploadServerSet.clear();
        }
    }

    @Override
    public boolean reloadConfig() throws Exception {
        return true;
    }

    @Override
    public boolean reloadData() throws Exception {
        Map<Integer, List<CurioLotteryNoticeData>> tempMap = new ConcurrentHashMap<>();
        Map<Long, List<CurioLotteryNoticeData>> map = new CurioLotteryNoticeDataDaoImpl().getCurioLotteryNoticeData();
        for (Map.Entry<Long, List<CurioLotteryNoticeData>> entry : map.entrySet()) {
            int groupId = entry.getKey().intValue();
            ServerGroup serverGroup = ServerGroupMgr.getServerGroupByServerId(entry.getKey());
            if(serverGroup != null && serverGroup.isCrossRank()) {
                groupId = serverGroup.getGroupId();
            }
            if(!tempMap.containsKey(groupId)){
                tempMap.put(groupId, new ArrayList<>());
            }
            tempMap.get(groupId).addAll(entry.getValue());
        }
        for (Map.Entry<Integer, List<CurioLotteryNoticeData>> entry : tempMap.entrySet()) {
            entry.getValue().sort(Comparator.comparingLong(CurioLotteryNoticeData::getTime));
        }
        noticeMap = tempMap;
        return true;
    }

    @Override
    public boolean stop() {
        return true;
    }

    @Override
    public boolean save() {
        for (Map.Entry<Integer, List<CurioLotteryNoticeData>> entry : noticeMap.entrySet()) {
            for (CurioLotteryNoticeData data : entry.getValue()) {
                if(data.isInsertOption()){
                    new CurioLotteryNoticeDataDaoImpl().add(data);
                }else if(data.isUpdateOption()){
                    new CurioLotteryNoticeDataDaoImpl().update(data);
                }
            }
        }
        return true;
    }

    public static void changeMaxScore(long serverId, Map<Integer, BigInteger> scoreMap, List<CurioProto.CurioItemMaxScoreUserTemp> userList) {
        int groupId = getGroupId(serverId);
        Map<Integer, BigInteger> groupScoreMap = getGroupScoreMap(groupId);
        boolean change = false;
        synchronized (groupScoreMap){
            for (Map.Entry<Integer, BigInteger> entry : scoreMap.entrySet()) {
                BigInteger old = groupScoreMap.getOrDefault(entry.getKey(), BigInteger.ZERO);
                if(entry.getValue().compareTo(old) > 0){
                    groupScoreMap.put(entry.getKey(), entry.getValue());
                    change = true;
                }
            }
        }

        updateUserTempList(userList, groupId);


        if(change){
            if(scoreMap.size() == 1){
                syncMaxScoreToServer(groupId, scoreMap, false);
                getLogger().info("分组id : {}， 藏品最高分更新 区服： {}, 大小 {}, 档位{}", groupId, serverId, scoreMap.size(), userList.size());
            }
        }
    }

    public static int getGroupId(long serverId) {
        int groupId = (int)serverId;
        ServerGroup serverGroup = ServerGroupMgr.getServerGroupByServerId(serverId);
        if(serverGroup != null && serverGroup.isCrossRank()){
            groupId = serverGroup.getGroupId();
        }
        return groupId;
    }

    /**
     * 分组内的都上传了，再同步
     */
    public static void syncMaxScoreToServer() {
        List<Integer> groupList = new ArrayList<>();
        synchronized (needSyncGroup){
            if(needSyncGroup.size() == 0){
                return;
            }
            Iterator<Integer> iterator = needSyncGroup.iterator();
            while (iterator.hasNext()){
                int groupId = iterator.next();
                boolean allUpload = isAllUpload(groupId);
                if(allUpload){
                    groupList.add(groupId);
                    iterator.remove();
                }
            }
        }
        for (Integer groupId : groupList) {
            Map<Integer, BigInteger> map = groupMaxCurioScoreMap.getOrDefault(groupId, new HashMap<>());
            syncMaxScoreToServer(groupId, map, true);
        }
    }

    public static boolean isAllUpload(int groupId) {
        List<Long> serverIdList = new ArrayList<>();
        ServerGroup serverGroup = ServerGroupMgr.getServerGroup(groupId);
        if(serverGroup == null || !serverGroup.isCrossRank()){
            serverIdList.add((long)groupId);
        }else {
            serverIdList.addAll(serverGroup.getServerList());
        }
        boolean allUpload = true;
        for (Long sid : serverIdList) {
            if(!uploadServerSet.contains(sid)){
                allUpload = false;
                break;
            }
        }
        return allUpload;
    }

    public static void syncMaxScoreToServer(int groupId, Map<Integer, BigInteger> scoreMap, boolean allSync) {
        if(scoreMap.size() > 0 || allSync){
            List<Long> serverList = new ArrayList<>();
            ServerGroup serverGroup = ServerGroupMgr.getServerGroup(groupId);
            if(serverGroup == null || !serverGroup.isCrossRank()){
                serverList.add((long)groupId);
            }else {
                serverList = serverGroup.getServerList();
            }
            CurioProto.CrossCurioMaxScoreSyncMsg.Builder sync = getMaxScoreSyncBuilder(scoreMap);
            sync.setAllSync(allSync);
            for (Long sid : serverList) {
                MessageHelper.sendPacket(sid, 0, YanQuMessageUtils.buildMessage(Protocol.S_CURIO_MAX_SCORE_SYNC_FROM_CROSS, sync));
                getLogger().info("分组id : {}， 藏品最高分 同步给 区服： {}, 大小 {}， 全量：{}", groupId, sid, scoreMap.size(), allSync);
            }
        }
    }

    public static CurioProto.CrossCurioMaxScoreSyncMsg.Builder getMaxScoreSyncBuilder(Map<Integer, BigInteger> scoreMap) {
        CurioProto.CrossCurioMaxScoreSyncMsg.Builder sync = CurioProto.CrossCurioMaxScoreSyncMsg.newBuilder();
        for (Map.Entry<Integer, BigInteger> entry : scoreMap.entrySet()) {
            CurioProto.CrossCurioMaxScoreTemp.Builder builder = CurioProto.CrossCurioMaxScoreTemp.newBuilder();
            builder.setScore(entry.getValue().longValue());
            builder.setCurioItemId(entry.getKey());
            sync.addItemScore(builder);
        }
        return sync;
    }

    public static void changeMaxScoreCondition(long serverId, List<CurioProto.CurioItemMaxScoreUserTemp> userList, Map<Integer, BigInteger> scoreMap) {

        int groupId = getGroupId(serverId);

        updateUserTempList(userList, groupId);


        Map<Integer, BigInteger> groupScoreMap = getGroupScoreMap(groupId);
        synchronized (groupScoreMap){
            for (Map.Entry<Integer, BigInteger> entry : scoreMap.entrySet()) {
                BigInteger old = groupScoreMap.getOrDefault(entry.getKey(), BigInteger.ZERO);
                if(entry.getValue().compareTo(old) > 0){
                    groupScoreMap.put(entry.getKey(), entry.getValue());
                }
            }
        }

        synchronized (uploadServerSet) {
            uploadServerSet.add(serverId);
        }

        synchronized (needSyncGroup){
            needSyncGroup.add(groupId);
        }
        getLogger().info("分组{}，区服{}，上传藏宝数据，最高分集合：{}，达成档位集合：{}", groupId, serverId, scoreMap.size(), userList.size());
        syncMaxScoreToServer();
    }

    private static void updateUserTempList(List<CurioProto.CurioItemMaxScoreUserTemp> userList, int groupId) {
        Map<Integer, CurioProto.CurioItemMaxScoreUserTemp> groupMap = groupHistoryConditionMap.get(groupId);
        if(groupMap == null){
            synchronized (groupHistoryConditionMap){
                groupMap = groupHistoryConditionMap.get(groupId);
                if(groupMap == null){
                    groupMap = new ConcurrentHashMap<>();
                    groupHistoryConditionMap.put(groupId, groupMap);
                }
            }
        }
        synchronized (groupMap){
            for (CurioProto.CurioItemMaxScoreUserTemp userTemp : userList) {
                CurioProto.CurioItemMaxScoreUserTemp temp = groupMap.get(userTemp.getConditionId());
                //不存在或者时间更早
                if(temp == null || userTemp.getUpdateTime() < temp.getUpdateTime()){
                    groupMap.put(userTemp.getConditionId(), userTemp);
                }
            }
        }
    }

    /**
     * 通知区服上传各藏品档位数据
     */
    public static void notifyServerUploadHistoryScore() {
        int channelId = ConfigHelper.getInt("channelId");
        List<ServerInfo> serverList = ServerListMgr.getChannelServerList(channelId);
        CurioProto.CrossCurioNotifyUploadHistoryItemScoreMsg.Builder builder = CurioProto.CrossCurioNotifyUploadHistoryItemScoreMsg.newBuilder();
        for (ServerInfo serverInfo : serverList) {
            if(serverInfo.isOpen()
                    && !uploadServerSet.contains(serverInfo.getServerId())){
                MessageHelper.sendPacket(serverInfo.getServerId(), 0, YanQuMessageUtils.buildMessage(Protocol.S_CURIO_HISTORY_MAX_SCORE_UPLOAD_NOTIFY, builder));
                getLogger().info("通知区服上传藏宝数据，{}", serverInfo.getServerId());
            }
        }
    }

    public static void getGroupHistoryMaxScore(long serverId, long playerId, List<Integer> conditionIdList) {
        int groupId = getGroupId(serverId);
        CurioProto.CurioItemMaxScoreGetRespMsg.Builder clientMsg = CurioProto.CurioItemMaxScoreGetRespMsg.newBuilder();
        clientMsg.setRet(0);
        Map<Integer, CurioProto.CurioItemMaxScoreUserTemp> tempMap = groupHistoryConditionMap.get(groupId);
        if(tempMap != null){
            for (Integer cid : conditionIdList) {
                CurioProto.CurioItemMaxScoreUserTemp temp = tempMap.get(cid);
                if(temp != null){
                    clientMsg.addScoreUser(temp);
                }
            }
        }
        MessageHelper.sendPacket(serverId, playerId, YanQuMessageUtils.buildMessage(Protocol.U_CURIO_HISTORY_MAX_SCORE_GET_USER_INFO, clientMsg));
    }

    public static Map<Integer, BigInteger> getGroupScoreMap(int groupId){
        Map<Integer, BigInteger> map = groupMaxCurioScoreMap.get(groupId);
        if(map == null){
            synchronized (groupMaxCurioScoreMap){
                map = groupMaxCurioScoreMap.get(groupId);
                if(map == null){
                    map = new ConcurrentHashMap<>();
                    groupMaxCurioScoreMap.put(groupId, map);
                }
            }
        }
        return map;
    }

    public static List<CurioLotteryNoticeData> getCurioLotteryNoticeData(int groupId){
        List<CurioLotteryNoticeData> list = noticeMap.get(groupId);
        if(list == null){
            synchronized (noticeMap){
                list = noticeMap.computeIfAbsent(groupId, k -> new ArrayList<>());
            }
        }
        return list;
    }

    /**
     * 添加公告
     */
    public static void uploadNotice(long userId, long serverId, String rewardId, int type, long time) {
        CurioLotteryNoticeData notice = new CurioLotteryNoticeData();
        notice.setServerId(serverId);
        notice.setUserId(userId);
        notice.setRewardId(rewardId);
        notice.setTime(time);
        notice.setType(type);
        notice.setInsertOption();

        List<Long> serverList = new ArrayList<>();
        int groupId = (int)serverId;
        ServerGroup serverGroup = ServerGroupMgr.getServerGroupByServerId(serverId);
        if(serverGroup != null && serverGroup.isCrossRank()){
            groupId = serverGroup.getGroupId();
            serverList.addAll(serverGroup.getServerList());
        }else {
            serverList.add(serverId);
        }

        List<CurioLotteryNoticeData> noticeList = getCurioLotteryNoticeData(groupId);
        synchronized (noticeList) {
            noticeList.add(notice);
            if(noticeList.size() > maxCount){
                noticeList.remove(0);
            }
        }

        // 同步到区服
        CurioProto.SyncCurioLotteryNoticeRespMsg.Builder builder = CurioProto.SyncCurioLotteryNoticeRespMsg.newBuilder();
        builder.setNotice(parseNoticeMsg(notice));
        YanQuMessage yanQuMessage = YanQuMessageUtils.buildMessage(Protocol.S_CURIO_SYNC_NOTICE, builder);
        for (Long sid : serverList) {
            MessageHelper.sendPacket(sid, 0, yanQuMessage);
        }
    }

    public static CurioProto.CurioLotteryNoticeMsg.Builder parseNoticeMsg(CurioLotteryNoticeData notice) {
        CurioProto.CurioLotteryNoticeMsg.Builder noticeBuilder = CurioProto.CurioLotteryNoticeMsg.newBuilder();
        noticeBuilder.setUserId(notice.getUserId());
        UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(notice.getUserId());
        noticeBuilder.setPlayerMsg(PlayerBasePb.parsePlayerBaseTempMsg(userBaseInfo));
        noticeBuilder.setGoodsId(notice.getRewardId());
        noticeBuilder.setType(notice.getType());
        noticeBuilder.setTime(notice.getTime());
        return noticeBuilder;
    }

    /**
     * 获取公告
     */
    public static List<CurioLotteryNoticeData> getNoticeData(int groupId) {
        List<CurioLotteryNoticeData> tmpList = getCurioLotteryNoticeData(groupId);
        if (tmpList.size() > maxCount) {
            List<CurioLotteryNoticeData> result = new ArrayList<>();
            int size = tmpList.size();
            for (int i = size - 1; i >= size - maxCount ; i--) {
                result.add(tmpList.get(i));
            }
            return result;
        } else {
            return new ArrayList<>(tmpList);
        }
    }
}
